
package SSF.OS;

import com.renesys.raceway.SSF.*;
import com.renesys.raceway.DML.*;

import java.io.*;
import java.util.*;

/** Protocol graph class providing some simple initialization and 
  * management functions for instances of class SSF.OS.ProtocolSession.
  * Each protocol graph is an entity, with in-channels and out-channels
  * for exchanging PacketEvents with the rest of the 
  * simulation.
  * A protocol graph is configured using the com.renesys.raceway.DML Configurable 
  * interface.  For example, the following DML flat file describes a 
  * simple protocol graph:<p>
  * <pre><code>
  * graph [
  *   description "Sample protocol graph, using trivial TCP/IP."
  *                
  *   ProtocolSession [
  *     name ip
  *     use SSF.OS.IP.ipSession
  *   ]
  *   ProtocolSession [
  *     name tcp
  *     use SSF.OS.TCP.tcpSession
  *   ]
  * ]
  * </code></pre><p>
  */
public class ProtocolGraph extends Entity 
implements Configurable {

  private java.util.Hashtable byName;
  private java.util.Hashtable globals;
  private java.util.Vector pendingInitialization;

  private Resource cpu;
  public Resource cpu() { return cpu; }

//----------------------------------------------------------------------

/** Optional string describing the protocol. */
  public String description;

  public ProtocolGraph() {
    byName = new Hashtable();
    globals = new Hashtable();
    pendingInitialization = new Vector();
  }

/** Configure the protocol graph by instantiating and configuring   
  * each named protocol session.
  */
    @Override
	public void config(Configuration cfg) throws configException {
	description = (String)cfg.findSingle("description");


	String usecpu;
	if (null!=(usecpu = (String)cfg.findSingle("cpudelay")) &&
	    true==Boolean.valueOf(usecpu).booleanValue()) 
	    cpu = new Resource();
	

	for (Enumeration sessions = cfg.find("ProtocolSession");
	     sessions.hasMoreElements();) 
	    configSession((Configuration)sessions.nextElement());
	
	for (Enumeration default_sessions = 
		 cfg.find(".global.ProtocolSession");
	     default_sessions.hasMoreElements();) {
	    Configuration default_session = 
		(Configuration)default_sessions.nextElement();
	    String default_name = 
		(String)default_session.findSingle("name");
	    if (null != default_session &&
		null == byName.get(default_name)) 
		configSession(default_session);
	}
    }
    
    private void configSession(Configuration sess) throws configException {
	String useClassName = (String)sess.findSingle("use");	
	
	ProtocolSession ps;
	
	try {
	    Class pclass = Class.forName(useClassName);
	    Object pobj = pclass.newInstance();
	    ps = (ProtocolSession) pobj;
	} catch (Exception any) {
	    System.err.println("Can't use ProtocolSession "+useClassName+":");
	    any.printStackTrace();
	    throw new configException
		("can't use ProtocolSession "+useClassName);
	}
	
	try {
	    ps.setGraph(this);
	} catch (ProtocolException pex) {
	    throw new configException("Can't configure ProtocolGraph: "+
				      pex.toString());
	}
	
	ps.config(sess);
	
	try {
	    if (!Class.forName("SSF.OS.ProtocolSession").
		isAssignableFrom(ps.getClass()))  
		throw new configException(ps.use+
					  " does not extend SSF.OS.ProtocolSession.");
	} catch (ClassNotFoundException cnfe) {
	    throw new configException("Cannot find class SSF.OS.ProtocolSession");
	}
	
	byName.put(ps.name,ps.getClass());
	globals.put(ps.getClass(),ps);
	pendingInitialization.addElement(ps);
    }
    
    
    /** Initialize all the protocols in the graph when the simulation begins. 
     */
    @Override
	public void init() {
	try {
	    int ct = pendingInitialization.size();
	    for (int ps = ct-1; ps>=0; ps--)
		((ProtocolSession)pendingInitialization.elementAt(ps)).init();
	    /*
	      for (Enumeration prots = byName.keys(); prots.hasMoreElements();) 
	      SessionForName((String)prots.nextElement()).init();
	    */
	} catch (ProtocolException pex) {
	    pex.printStackTrace();
	    System.exit(-1);
	}
    }
    
 /** If a protocol has been registered under the given name, return an 
   * instance (session) of it.  Otherwise, create a new session of the 
   * protocol and return that. 
@exception ProtocolException if the specified protocol name doesn't name a valid protocol class, or if the class could not be instantiated due to its lack of a public zero-argument constructor. 
   */
  public ProtocolSession SessionForName(String protocol_name) 
    throws ProtocolException {
      ProtocolSession instance;
      Vector downlist = null;

      Class useclass = (Class)byName.get(protocol_name);

      if (useclass == null) 
	throw new ProtocolException("in "+this+": protocol "+protocol_name+
				    " has no registered implementation");
	
	if (null == (instance = (ProtocolSession)globals.get(useclass)))
	  try {
	    instance = (ProtocolSession)useclass.newInstance(); 

	    if (globals.get(instance.getClass())!=null) {
	      throw new ProtocolException("Cannot register new instance of "+
					  instance.getClass()+
					  "; one is already available.");
	    }
	    else globals.put(instance.getClass(),instance);

	  } catch (Exception e) {
	    throw new ProtocolException(e+": "+useclass+
					" could not be instantiated");
	  }
      
/*
      if (downlist != null) 
	for (Enumeration dlist=downlist.elements();dlist.hasMoreElements();)
	  SessionForName((String)dlist.nextElement()).open(instance,null);
*/
      
      return(instance); 
    }
}

/*=                                                                      =*/
/*=  Copyright (c) 1997--2000  SSF Research Network                      =*/
/*=                                                                      =*/
/*=  SSFNet is open source software, distributed under the GNU General   =*/
/*=  Public License.  See the file COPYING in the 'doc' subdirectory of  =*/
/*=  the SSFNet distribution, or http://www.fsf.org/copyleft/gpl.html    =*/
/*=                                                                      =*/
